 /*
 Copyright (C) 2024 HiHope Open Source Organization .
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "cmsis_os2.h"         // CMSIS-RTOS API V2

#include "MQTTClient.h"        // MQTTClient-C库接口文件
#include "mqtt_ohos.h"         // OHOS(LiteOS)适配接口文件
#include "cJSON.h"             // cJson解析三方库
#include "mqtt_task.h"         // MQTT接口

// 控制MQTT任务循环
static volatile int running = 1;

Network network;
MQTTClient client;
int beep = 0;
int door = 0;
int fan = 0;
int light = 0;


// 发送和接收的数据缓冲区
#define BUFF_SIZE (2048)
static unsigned char sendbuf[BUFF_SIZE], readbuf[BUFF_SIZE];


int mqttMutexLock(Mutex* m) {
    return MutexLock(m);
}
int mqttMutexUnlock(Mutex* m) {
    return MutexUnlock(m);
}

/// @brief MQTT任务循环
/// @param arg MQTTClient
static void MqttTask(void *arg)
{
    // 输出日志
    printf("[mqtt_task] MqttTask start:\r\n");

    // 转换参数的类型
    MQTTClient *c = (MQTTClient *)arg;

    // 任务循环
    while (c) {
        // 检查是否需要退出任务循环
        mqttMutexLock(&c->mutex);
        if (!running) {
            // 需要退出任务循环
            printf("[mqtt_task] MqttTask: MQTT background thread exit!\r\n");
            mqttMutexUnlock(&c->mutex);
            break;
        }
        mqttMutexUnlock(&c->mutex);

        // 尝试接收数据
        mqttMutexLock(&c->mutex);
        if (c->isconnected) {
            // printf("[mqtt_task] MqttTask: MQTT isconnected!\r\n");
            MQTTYield(c, 1);
        }
        mqttMutexUnlock(&c->mutex);

        // 等待1秒
        sleep(1);
        //printf("[mqtt_task] MqttTask: MQTT looping!\r\n");
    }

    // 输出日志
    printf("[mqtt_task] MqttTask exit!\r\n");
}

/// 初始化并启动MQTT任务
void MqttTaskInit(void)
{
    // 初始化MQTT客户端
    NetworkInit(&network);
    MQTTClientInit(&client, &network, 2000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));

    // 开启MQTT任务循环
    running = 1;

    // 创建MQTT任务线程
    int rc = ThreadStart(&client.thread, MqttTask, &client);

    // 输出日志
    printf("[mqtt_task] MqttTaskInit done!\r\n");
}

/// 停止MQTT任务
void MqttTaskDeinit(void)
{
    mqttMutexLock(&client.mutex);
    // 停止MQTT任务循环
    running = 0;
    mqttMutexUnlock(&client.mutex);
    mqttMutexDeinit(&client.mutex);
}

/// 连接MQTT服务器
/// @param[in] host 服务器地址
/// @param[in] port 服务器端口
/// @param[in] clientId 客户端ID
/// @param[in] username 用户名
/// @param[in] password 密码
/// @return 0：成功，-1：失败
int MqttTaskConnect(const char *host, unsigned short port,
                    const char *clientId, const char *username, const char *password)
{
    // 用于接收接口返回值
    int rc = -1;

    // 初始化MQTT连接信息
    MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;

    // 尝试连接网络
    if ((rc = NetworkConnect(&network, host, port)) != 0) {
        printf("[mqtt_task] MqttTaskConnect: NetworkConnect NG\r\n");
        printf("[mqtt_task] MqttTaskConnect: -----------------\r\n");
        return -1;
    }

    connectData.MQTTVersion       = 3;
    connectData.clientID.cstring  = (char *)clientId;
    connectData.username.cstring  = (char *)username;
    connectData.password.cstring  = (char *)password;
    connectData.keepAliveInterval = 60;
    connectData.cleansession      = 1;
    connectData.willFlag          = 0;

    // 发送MQTT连接包
    if ((rc = MQTTConnect(&client, &connectData)) != 0) {
        // 连接失败，输出日志并返回-1
        printf("[mqtt_task] MqttTaskConnect: MQTTConnect NG, rc[%d]\r\n", rc);
        printf("[mqtt_task] MqttTaskConnect: ======================\r\n");
        return -1;
    }

    // 成功连接到MQTT服务器，输出日志并返回0
    printf("[mqtt_task] MqttTaskConnect: MQTTConnect OK!\r\n");
    return 0;
}

// 消息到达的回调函数
static void OnMessageArrived(MessageData *data)
{
    int rc = -1;
    cJSON *obj_cmdname;
    cJSON *obj_paras;
    cJSON *obj_para;
    cJSON *obj_root = cJSON_ParseWithLength((char *)data->message->payload, data->message->payloadlen);
    if (obj_root == NULL) {
        printf("[mqtt_task] OnMessageArrived: Error parsing JSON obj_root\r\n");
        return;
    }

    // 获取命令名称
    obj_cmdname = cJSON_GetObjectItem(obj_root, "command_name");
     if (obj_cmdname == NULL) {
        printf("[mqtt_task] OnMessageArrived: Error parsing JSON obj_cmdname\r\n");
        cJSON_Delete(obj_root);
    }

    //命令名称：DoSomthing
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "DoSomthing") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            printf("[mqtt_task] OnMessageArrived: Error parsing JSON obj_paras\r\n");
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "args");//参数名称：args
        if (obj_para == NULL) {
            printf("[mqtt_task] OnMessageArrived: Error parsing JSON obj_para\r\n");
            cJSON_Delete(obj_root);
        }
        if (strcmp(cJSON_GetStringValue(obj_para), "DO") == 0) {//数据类型：string
            printf("[mqtt_task] OnMessageArrived: get cmd: DoSomthing(DO)\r\n");
        } else {
            printf("[mqtt_task] OnMessageArrived: get cmd: DoSomthing(UNDO)\r\n");
        }
        rc = 0;
    }

    // Beep
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Beep") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "ON_OFF");
        if (obj_para == NULL) {
            cJSON_Delete(obj_root);
        }
        ///< operate the LED here
        if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0) {
            beep = 1;
            IoTGpioSetOutputVal(BEEP_ON_OFF_CTRL_GPIO, 1);
            printf("[mqtt_task] OnMessageArrived: get cmd: Beep(ON)\r\n");
        } else {
            beep = 0;
            IoTGpioSetOutputVal(BEEP_ON_OFF_CTRL_GPIO, 0);
            printf("[mqtt_task] OnMessageArrived: get cmd: Beep(OFF)\r\n");
        }
        rc = 0;
    }

    //Door
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Door") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "ON_OFF");
        if (obj_para == NULL) {
            cJSON_Delete(obj_root);
        }
        ///< operate the LED here
        if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0) {
            door = 1;
            IoTGpioSetOutputVal(DOOR_ON_OFF_CTRL_GPIO, 1);
            printf("[mqtt_task] OnMessageArrived: get cmd: Door(Open)\r\n");
        } else {
            door = 0;
            IoTGpioSetOutputVal(DOOR_ON_OFF_CTRL_GPIO, 0);
            printf("[mqtt_task] OnMessageArrived: get cmd: Door(Close)\r\n");
        }
        rc = 0;
    }

    // Fan
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Fan") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "ON_OFF");
        if (obj_para == NULL) {
            cJSON_Delete(obj_root);
        }
        ///< operate the LED here
        if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0) {
            fan = 1;
            IoTGpioSetOutputVal(FAN_ON_OFF_CTRL_GPIO, 1);
            printf("[mqtt_task] OnMessageArrived: get cmd: Fan(ON)\r\n");
        } else {
            fan = 0;
            IoTGpioSetOutputVal(FAN_ON_OFF_CTRL_GPIO, 0);
            printf("[mqtt_task] OnMessageArrived: get cmd: Fan(OFF)\r\n");
        }
        rc = 0;
    }

    //Light
    if (strcmp(cJSON_GetStringValue(obj_cmdname), "Light") == 0) {
        obj_paras = cJSON_GetObjectItem(obj_root, "paras");
        if (obj_paras == NULL) {
            cJSON_Delete(obj_root);
        }
        obj_para = cJSON_GetObjectItem(obj_paras, "ON_OFF");
        if (obj_para == NULL) {
            cJSON_Delete(obj_root);
        }
        ///< operate the LED here
        if (strcmp(cJSON_GetStringValue(obj_para), "ON") == 0) {
            light = 1;
            IoTGpioSetOutputVal(LIGHT_ON_OFF_CTRL_GPIO, 1);
            printf("[mqtt_task] OnMessageArrived: get cmd: Light(ON)\r\n");
        } else {
            light = 0;
            IoTGpioSetOutputVal(LIGHT_ON_OFF_CTRL_GPIO, 0);
            printf("[mqtt_task] OnMessageArrived: get cmd: Light(OFF)\r\n");
        }
        rc = 0;
    }

#if RESPONSE_DBG  //print topic and payload info, response for cmd
    printf("[mqtt_task] OnMessageArrived: **************************************\r\n");
    int   topic_len   = data->topicName->lenstring.len;
    char* topic_name  = malloc(topic_len + 1);
    int   payload_len = data->message->payloadlen;
    char* payload_str = malloc(payload_len + 1);

    char* response_topic   = malloc(topic_len + 1 + 9);
    char* response_payload = malloc(PAYLOAD_BUFF_LEN);
    if (topic_name == NULL || payload_str == NULL ||
        response_topic == NULL || response_payload == NULL) {
        goto EXIT;
    }
    memset(topic_name, 0, topic_len + 1);
    memset(payload_str, 0, payload_len + 1);
    memset(response_topic, 0, topic_len + 1 + 9);
    memset(response_payload, 0, PAYLOAD_BUFF_LEN);
    memcpy(topic_name, data->topicName->lenstring.data, topic_len);
    memcpy(payload_str, (char*)data->message->payload,  payload_len);
    printf("recv:   topic: [%3d][%s]\r\n", topic_len,   topic_name);
    printf("recv: payload: [%3d][%s]\r\n", payload_len, payload_str);

    char* reqid = strstr(topic_name, "request_id=");
    if (reqid != NULL) {
        memcpy(response_topic, topic_name, reqid-topic_name);
        memcpy(response_topic + (reqid-topic_name), "response/", strlen("response/"));
        memcpy(response_topic + (reqid-topic_name+strlen("response/")), reqid, topic_name+topic_len-reqid);
    }

    sprintf(response_payload, "{\"result_code\":%d,\"response_name\":\"%s\",\"paras\":{\"result\":\"success\"}}",
        rc, cJSON_GetStringValue(obj_cmdname));

    printf("resp:   topic: [%3d][%s]\r\n", strlen(response_topic), response_topic);
    printf("resp: payload: [%3d][%s]\r\n", strlen(response_payload), response_payload);

    rc = MqttTaskPublish(response_topic, response_payload);
    printf("[mqtt_task] OnMessageArrived: MqttTaskPublish(response) %s!\r\n",(rc == 0)?"OK":"NG");
    printf("[mqtt_task] OnMessageArrived: **************************************\r\n\r\n");

EXIT:
    if (topic_name != NULL) {
        free(topic_name);
    }
    if (payload_str != NULL) {
        free(payload_str);
    }
    if (response_topic != NULL) {
        free(response_topic);
    }
    if (response_payload != NULL) {
        free(response_payload);
    }
#endif

    cJSON_Delete(obj_root);
}

/// 订阅主题
/// @param topic 主题
int MqttTaskSubscribe(char *topic)
{
    int rc = 0;

    printf("[mqtt_task] MqttTaskSubscribe: Subscribe: topic[%s] from broker\r\n", topic);

    if ((rc = MQTTSubscribe(&client, topic, 0, OnMessageArrived)) != 0) {
        printf("[mqtt_task] MqttTaskSubscribe: Subscribe failed: %d\r\n", rc);
    }

    return rc;
}

/// 发布消息
/// @param topic 主题
/// @param payload 消息内容
int MqttTaskPublish(char *topic, char *payload)
{
    int rc = 0;

    MQTTMessage message;
    message.qos = QOS2;
    message.retained = 0;
    message.payload = payload;
    message.payloadlen = strlen(payload);

    printf("[mqtt_task] MqttTaskPublish: ################################\r\n");
    printf("        topic: [%3d][%s]\r\n", strlen(topic), topic);
    printf("      payload: [%3d][%s]\r\n", strlen(payload), payload);

    if ((rc = MQTTPublish(&client, topic, &message)) != 0) {
        printf("[mqtt_task] MqttTaskPublish: MQTTPublish failed: %d\r\n", rc);
    }
    printf("[mqtt_task] MqttTaskPublish: ################################\r\n");

    return rc;
}

/// 断开和MQTT服务器的连接
int MqttTaskDisconnect(void)
{
    int rc = 0;

    if ((rc = MQTTDisconnect(&client)) != 0) {
        printf("[mqtt_task] MqttTaskDisconnect: MQTTDisconnect failed: %d\r\n", rc);
        return -1;
    }

    NetworkDisconnect(&network);

    return 0;
}

/// 重连MQTT服务器
int MqttTaskReconnect(const char *host, unsigned short port,
                      const char *clientId, const char *username, const char *password)
{
    // 断开当前连接
    MqttTaskDisconnect();

    // 尝试重新连接，最多重试3次
    for (int i = 0; i < 3; i++) {
        if (MqttTaskConnect(host, port, clientId, username, password) == 0) {
            printf("[mqtt_task] MqttTaskReconnect: MqttTaskConnect to MQTT server!\r\n");
            return 0;
        } else {
            printf("[mqtt_task] MqttTaskReconnect: Reconnect attempt %d failed, retrying...\r\n", i + 1);
            sleep(2); // 等待2秒后重试
        }
    }

    printf("[mqtt_task] MqttTaskReconnect: Failed to Reconnect to MQTT server after 3 attempts.\r\n");
    return -1;
}
